<?php


defined('BASEPATH') OR exit('No direct script access allowed');

/*
 * 提交订单
 *      |
 * 选择支付方式
 *      |
 * 调用支付客户端
 *      |                                   \
 * 客户端支付成功                          客户端支付失败
 *      |               \                   |                   \
 * 同步唤醒轻盈客户端   异步通知轻盈服务端    同步唤醒轻盈客户端       异步通知轻盈服务端
 *
 */
class DDConst
{
    // 订单状态
    const STATUS_ADD                        = 0; // 提交订单
    const STATUS_PREPAY                     = 1; // 选择支付方式后 等待付款
//    const STATUS_PREPAY_AND_CANCEL          = 2; // 等待付款时取消订单

    const STATUS_EXPIRE                     = 11; // 支付过期

    const STATUS_PAY_ASYNC_SUCCESS          = 21; // 成功 订单异步回调


    // 订单类型
    const TYP_EXPERT_DIAGNOSE               = 1; // 专家问诊贴

    // 支付类型
    const PAY_TYP_ALIPAY                    = 1; // 支付宝

    // 支付信息标题
    const PAY_INFO_TITLE                    = '轻盈医学-远程咨询';
}


class Dingdanm extends My_Model
{
    private $from_dingdan = array();
    private $from_status = null;
    private $update_data = array();
    private $to_dingdan = array();
    private $to_status = null;

    function __construct()
    {
        parent::__construct();
        $this->load->helper('alipay_utils');
    }

    /*********************** DB utils *************************/


    public function updateDBById($id, $update_data)
    {
        $update_data["mtime"] = time();
        return $this->getTableInstance(self::DB_NAME_DINGDAN)
            ->updateByCondition(array("id"=>$id), $update_data);
    }

    public function getDBById($id)
    {
        return $this->getTableInstance(self::DB_NAME_DINGDAN)
            ->selectOneByConditionWithOrder(array("id"=>$id));
    }

    public function getDBByDDId($dingdan_id)
    {
        return $this->getTableInstance(self::DB_NAME_DINGDAN)
            ->selectOneByConditionWithOrder(array("dingdan_id"=>$dingdan_id), array("id"=>"desc"));
    }

    // 直接调用,注意得到的可能是已超时的订单,要自行处理
    public function getDBByRelateIdAndType($relate_id, $dingdan_type)
    {
        return $this->getTableInstance(self::DB_NAME_DINGDAN)
            ->selectOneByConditionWithOrder(
                array("relate_id"=>$relate_id, "dingdan_type"=>$dingdan_type),
                array("id"=>"desc"));
    }

    public function getZhuanjiaPostDBByPostId($post_id)
    {
        return $this->getTableInstance(self::DB_NAME_POSTS)
            ->selectOneByConditionWithOrder(array("id"=>$post_id, "post_type"=>1));
    }


    /*********************** Logic *************************/

    /**
     * 订单 id 生成
     * @return string
     */
    public function genDingdanId() {
        ci_load_once('tradem');
        $dingdan_id = $this->tradem->genTradeID(Tradem::BUSINESS_ZIPCODE_EXPORT_POST);
        return $dingdan_id;
    }

    public function addPayLogByDDinfo($dingdan_info) {
        $time = time();
        $pay_data = array();
        $pay_data["dingdan_id"] = $dingdan_info["dingdan_id"];
        $pay_data["pay_method"] = $dingdan_info["pay_method"];
        $pay_data["user_id"] = $dingdan_info["user_id"];
        $pay_data["price"] = $dingdan_info["price"];
        $pay_data["status"] = $dingdan_info["status"];
        $pay_data["transaction_id"] = $dingdan_info["transaction_id"];

        $pay_data["atime"] = $time;

        $id = $this->getTableInstance(self::DB_NAME_PAY_LOG)->insertOne($pay_data);
        return $id;
    }

    public function createDingdan($param) {
        $error_prefix = "订单创建失败:";
        if (!isset($param["relate_id"])) {
            return array("code"=>0, "tips"=>$error_prefix."relate_id不能为空");
        }
        if (!isset($param["dingdan_type"])) {
            return array("code"=>0, "tips"=>$error_prefix."订单类型不能为空");
        }
        if (!isset($param["price"]) || !floatval($param["price"])) {
            return array("code"=>0, "tips"=>$error_prefix."订单价格不能为空");
        }
        if (!isset($param["zhuanjia_id"]) || !intval($param["zhuanjia_id"])) {
            return array("code"=>0, "tips"=>$error_prefix."专家id不能为空");
        }
        if (!isset($param["user_id"]) || !intval($param["user_id"])) {
            return array("code"=>0, "tips"=>$error_prefix."用户id不能为空");
        }
        if (!isset($param["zhuanjia_id"]) || !intval($param["zhuanjia_id"])) {
            return array("code"=>0, "tips"=>$error_prefix."专家id不能为空");
        }
        if (!isset($param["contact_name"]) || empty($param["contact_name"])) {
            return array("code"=>0, "tips"=>$error_prefix."联系人名称不能为空");
        }
        if (!isset($param["phone"]) || empty($param["phone"])) {
//            return array("code"=>0, "tips"=>$error_prefix."联系人手机号码不能为空");
        }
        $time = time();
        $dingdan_id = $this->genDingdanId();
        $dingdan_data = array();
        $dingdan_data["dingdan_id"] = $dingdan_id;
        $dingdan_data["relate_id"] = $param["relate_id"];
        $dingdan_data["dingdan_type"] = $param["dingdan_type"];
        $dingdan_data["price"] = $param["price"];
        $dingdan_data["status"] = DDConst::STATUS_ADD;
        $dingdan_data["user_id"] = intval($param["user_id"]);
        $dingdan_data["zhuanjia_id"] = intval($param["zhuanjia_id"]);

        // 地址信息
        $dingdan_data["contact_name"] = $param["contact_name"];
        $dingdan_data["phone"] = $param["phone"];
        $dingdan_data["address"] = isset($param["address"]) ? $param["address"] : "";
        if (isset($param["address_id"])) {
            $dingdan_data["address_id"] = $param["address_id"];
        }

        // 设备信息
        $dingdan_data["os_type"] = isset($param["os_type"]) ? $param["os_type"] : "";
        $dingdan_data["version"] = isset($param["version"]) ? $param["version"] : "";
        $dingdan_data["atime"] = $time;
        $dingdan_data["mtime"] = $time;

        $id = $this->getTableInstance(self::DB_NAME_DINGDAN)->insertOne($dingdan_data);
        $dingdan_data["id"] = $id;

        // ORDER LOG

        return array("code"=>1, "dingdan_id"=>$dingdan_id, "dingdan_info"=>$dingdan_data);
    }

    // 更新专家帖状态为已付款
    public function updateZJPAsPaiedByPostId($post_id) {
        $this->load->model('postm');
        $this->postm->update_post(array("id"=>$post_id), array("paid_success"=>1));
        // 更新帖子缓存
        $this->removeCache(Postm::MC_KEY_POST4SEARCH . $post_id);
    }

    public function onPaySuccessTypeExpert($dingdan_info) {
        // 更新专家帖中付款状态
        $this->updateZJPAsPaiedByPostId($dingdan_info["relate_id"]);

        // 发送订单信息 push
//        $push_result = $this->jpushmd->SendByAlias(
//            $dingdan_info["user_id"], "",//$message,
//            array('post_id'=>$dingdan_info["relate_id"],'type'=>0));
    }

    // 支付宝支付过期时间分钟剩余,单位分钟
    public function getAlipayExpireMinLeft($dingdan_info) {
        if (empty($dingdan_info["atime"])) {
            return PAY_ALI_EXPIRE;
        }
        $result = (int)(PAY_ALI_EXPIRE - (time() - (int)$dingdan_info["atime"]) / 60);
        if ($result <= 0) {
            $result = 1;
        }
        return $result;
    }

    // 订单过期时间戳
    public function getQYExpireTimestamp($dingdan_info) {
        $min_delta = PAY_ALI_EXPIRE + PAY_ALI_EXPIRE_DELAY;
        if (empty($dingdan_info["atime"])) {
            return time() + $min_delta * 60;
        }

        return (int)$dingdan_info["atime"] + 60 * $min_delta;
    }

    // 生成移动支付付款字符串
    public function getMobilePayStr4Alipay($dingdan_info)
    {
        $this->load->model('ali_mobilepaym');

        return $this->ali_mobilepaym->genQuotePayInfo(
            $dingdan_info["dingdan_id"], DDConst::PAY_INFO_TITLE,
            DDConst::PAY_INFO_TITLE, $dingdan_info["price"],
            $this->getAlipayExpireMinLeft($dingdan_info)
        );
    }

    /**
     * 获取专家帖订单状态
     * 1. 订单支付完成paid_success=1
     * 2. 订单超时或无相关订单,订单信息不可用,返回dingdan_info=array()
     * @param $post_id
     * @return array
     */
    public function getPostPayStatusByPostId($post_id)
    {
        $dingdan_info = $this->getDBByRelateIdAndType(
            $post_id, DDConst::TYP_EXPERT_DIAGNOSE);

        // 有订单
        if (isset($dingdan_info["id"])) {
            $status = $dingdan_info["status"];

            // 判断订单是否支付完成
            if ($status == DDConst::STATUS_PAY_ASYNC_SUCCESS) {
                return array("code" => 1, "tips" => "已付款",
                    "paid_success"=>1, "dingdan_info" => $dingdan_info);
            }

            // 判断订单是否过期, 比给支付过期时间稍长5min
            if (time() >= $this->getQYExpireTimestamp($dingdan_info)) {
                $status = DDConst::STATUS_EXPIRE;
                $this->updateDingdanStatus($dingdan_info, $status);
            }

            // 判断订单是否过期
            if ($status == DDConst::STATUS_EXPIRE) {
                return array("code" => 1, "tips" => "订单超时失效",
                    "paid_success"=>0, "dingdan_info"=>array());
            }
            return array("code" => 1, "tips" => "待付款",
                "paid_success"=>0, "dingdan_info"=>$dingdan_info);
        }
        return array("code" => 0, "tips" => "无订单信息",
            "paid_success"=>0, "dingdan_info"=>array());
    }



    /*********************** 订单状态变更相关 使用类变量辅助判断 *************************/


    private function isOnPaySuccessCallback() {
        if (isset($this->update_data["transaction_id"]) &&
            $this->to_status == DDConst::STATUS_PAY_ASYNC_SUCCESS) {
            return true;
        }
        return false;
    }

    private function isPayAction() {
        if (($this->to_status==DDConst::STATUS_PREPAY) ||
            ($this->to_status==DDConst::STATUS_PAY_ASYNC_SUCCESS)) {
            return true;
        }
        return false;
    }

    // 初始化类订单变量
    private function initUpdateDingdanStatus($origin_dingdan, $status_to, $update_data) {
        $this->from_dingdan = $origin_dingdan;
        $this->from_status = $origin_dingdan["status"];
        $this->to_status = $status_to;

        $update_data["status"] = $status_to;
        $update_data["mtime"] = time();
        $this->update_data = $update_data;

        $this->to_dingdan = $this->from_dingdan;

        $this->updateTheUpdateData($update_data);
    }

    private function updateTheUpdateData($updata_data) {
        $this->update_data = array_merge($this->update_data, $updata_data);
        $this->to_dingdan = array_merge($this->to_dingdan, $this->update_data);
    }

    private function beforeUpdateDingdanStatus() {
        if ($this->to_status == DDConst::STATUS_PREPAY) {
            if (empty($this->from_dingdan["qr_image"])) {
                $this->load->model('ali_fuwuchuangm');
                // 生成支付二维码,更新订单状态为:待支付,存储此支付二维码信息
                $qr_image = $this->ali_fuwuchuangm->genAliPrepayQRData(
                    $this->to_dingdan["dingdan_id"], DDConst::PAY_INFO_TITLE,
                    DDConst::PAY_INFO_TITLE, $this->to_dingdan["price"],PAY_ALI_NOTIFY,
                    $this->getAlipayExpireMinLeft($this->to_dingdan));
                $this->updateTheUpdateData(array("qr_image"=>$qr_image['qr_image']));
            }
        }
    }

    private function afterUpdateDingdanStatus() {
        if ($this->isOnPaySuccessCallback()) {
            if ($this->to_dingdan["dingdan_type"] == DDConst::TYP_EXPERT_DIAGNOSE) {
                // 如果是专家会诊帖, 触发支付成功
                $this->onPaySuccessTypeExpert($this->to_dingdan);
            }
        }

        // 清理帖子缓存
        ci_load_once('clear_cachem');
        $this->clear_cachem->clear_post($this->to_dingdan['relate_id']);

        // 预支付或支付完成, 记录支付日志
        if ($this->isPayAction()) {
            $this->addPayLogByDDinfo($this->to_dingdan);
        }

        // 日志记录
        $log_msg = "Status_change:{$this->from_status}-{$this->to_status} " .
            " origin dingdan:" . json_encode($this->from_dingdan, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) .
            " update data:" . json_encode($this->update_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        paylog($log_msg, "dingdan_status", $this->to_dingdan["dingdan_id"]);

    }

    // 订单状态转换及转换前后动作触发
    public function updateDingdanStatus($dingdan_info, $status_to, $update_data = array()) {
        // 初始化
        $this->initUpdateDingdanStatus($dingdan_info, $status_to, $update_data);
        // 在更新之前 可以做一些更新数据准备及判断
        $this->beforeUpdateDingdanStatus();

        $this->updateDBById($dingdan_info["id"], $this->update_data);
        // 数据更新之后的其他数据更新 事件触发
        $this->afterUpdateDingdanStatus();

        return $this->to_dingdan;
    }


}